package com.novelot.plugin.gradle

import com.google.gson.Gson
import org.gradle.BuildAdapter
import org.gradle.BuildResult
import org.gradle.api.Project
import java.io.File
import java.nio.charset.Charset
import java.util.*

class Diff(private val project: Project) {

    private var codeDirPath: String
    private var jsonFilePath: String

    init {
        val module = project.path.replace(":", "")
        jsonFilePath = "${project.rootDir}/.nove/$module/snapshot.json"
        codeDirPath = "${project.projectDir}/src/main/java"

        project.gradle.addBuildListener(object : BuildAdapter() {
            override fun buildFinished(result: BuildResult) {
                super.buildFinished(result)
                if (result.failure == null) {
                    scanDirAndGenSnapshotAndSaveToFile(project)
                }
            }
        })
    }

    fun diff() {
        val nodeRoot = loadSnapshotFromDisk(jsonFilePath)
        if (nodeRoot == null) {
            Log.v(str = "node is null")
        }
        val diffSet = searchDiffInDir(nodeRoot, codeDirPath)
        System.out.println("发生变化文件的集合:")
        diffSet.forEach {
            System.out.println("[${it}]")
        }


    }


    private fun searchDiffInDir(nodeRoot: Node?, dir: String): Set<String> {
        if (nodeRoot == null) return emptySet()
        val diffSet = mutableSetOf<String>()

        val changedNode = LinkedList<Node>()
        changedNode.add(nodeRoot)

        while (changedNode.isNotEmpty()) {
            val node = changedNode.removeFirst()
            val file = File(node.path)
            if (!file.exists()) {
                //该节点下的树,被全部删除
                //将整颗树加进去
                Log.v(str = "该节点下的树,被全部删除,将整颗树加到set进去")
                addTreeToSet(node, diffSet)
            } else {
                //
                if (node.lastModified != file.lastModified()) {
                    if (file.isDirectory) {
                        if (node.childs.isNotEmpty()) {
                            val childPaths = node.childs.map {
                                it.path
                            }

                            file.listFiles().forEach {
                                if (childPaths.contains(it.absolutePath)) {
                                    //如果包含,则加入集合,下次循环时再比较
                                    Log.v(str = "如果包含,则加入集合,下次循环时再比较")
                                    changedNode.addAll(node.childs)
                                } else {
                                    //如果该file,不包含在之前的child集合中,说明该file是新建的
                                    Log.v(str = "如果该file,不包含在之前的child集合中,说明该file是新建的")
                                    addFileToSet(it, diffSet)
                                }
                            }

                        } else {
                            //之前的node下没有文件
                            //说明该路径下有新建文件或文件夹
                            Log.v(str = "之前的node下没有文件,说明该路径下有新建文件或文件夹")
                            addFileToSet(file, diffSet)
                        }
                    } else {
                        diffSet.add(node.path)
                    }
                }
            }
        }

        return diffSet
    }

    private fun addFileToSet(file: File, diffSet: MutableSet<String>) {
        if (file.isDirectory) {
            file.listFiles().forEach {
                addFileToSet(it, diffSet)
            }
        } else {
            diffSet.add(file.absolutePath)
        }
    }

    private fun addTreeToSet(node: Node, diffSet: MutableSet<String>) {
        if (node.childs.isEmpty()) {
            //叶子节点
            diffSet.add(node.path)
        } else {
            //文件夹被整个删除了
            //被删除了
            node.childs.forEach {
                addTreeToSet(it, diffSet)
            }
        }
    }

    private fun loadSnapshotFromDisk(jsonFilePath: String): Node? {
        Log.v(str = "从文件[${jsonFilePath}]加载Node...")

        val file = File(jsonFilePath)
        if (!file.exists()) {
            file.parentFile.mkdirs()
            file.createNewFile()
        }

        val json = file.readText(Charset.forName("UTF-8"))
        return Gson().fromJson<Node>(json, Node::class.java)
    }

    private fun scanDirAndGenSnapshotAndSaveToFile(project: Project) {
        val projectDir = File(codeDirPath)
        val nodeRoot = Node(0, projectDir.absolutePath, projectDir.lastModified())
//        scanDir(projectDir, nodeRoot)
        projectDir.loadToNode(nodeRoot)

        nodeRoot.saveToDisk(jsonFilePath)
    }

    private fun scanDir(f: File, root: Node) {
        val level = root.level + 1
        f.listFiles()
            .filter {
                it.isDirectory || it.extension in listOf("java", "kt")
            }
            .forEach {
                if (it.isDirectory) {
                    val child = Node(level, it.absolutePath, it.lastModified())
                    root.childs.add(child)
                    scanDir(it, child)
                } else {
                    root.childs.add(Node(level, it.absolutePath, it.lastModified()))
                }
            }
    }

}
